package com.personal.dataconvert;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.personal.core.data.DataRow;
import com.personal.core.data.DataTable;
import com.personal.core.utils.Assert;
import com.personal.core.utils.CoreUtil;
import com.personal.core.utils.StringUtil;
import com.personal.dataconvert.bean.HeaderConfig;
import com.personal.dataconvert.bean.HtmlConfig;
import com.personal.dataconvert.port.Data2Html;
import com.personal.dataconvert.util.CombineDataTableUtil;
import com.personal.dataconvert.util.ExcelHtmlUtil;

/**
 * 将数据根据配置导出至Html
 * @author cuibo
 */
public class ExportData2Html implements Data2Html
{
	/** 所有的数据 */
	private List<Map<String, Object>> allData = null;

	/** 所有的数据 */
	private List<?> allObjectData = null;

	/** 配置ID：cb先忽略转HTML的配置 */
	@SuppressWarnings("unused")
	private String configId;

	/** 配置信息 */
	private HtmlConfig htmlConfig;

	/** 处理同名问题 */
	private Map<String, String> sameNameCache;
	
	/** 开始索引 */
    private int startIndex = -1;
    
    /** 结束索引 */
    private int endIndex = Integer.MAX_VALUE;

	private ExportData2Html()
	{
	}

	private ExportData2Html(Builder builder)
	{
		this.configId = builder.configId;
		this.allObjectData = builder.allObjectData;
		this.htmlConfig = builder.htmlConfig;
		this.sameNameCache = builder.sameNameCache;
		this.startIndex = builder.startIndex;
		this.endIndex = builder.endIndex;
		convertObject(allObjectData);
		if (htmlConfig != null)
		{
			// 设置深度等信息，防止使用者传过来非法数据
			ExcelHtmlUtil.setHtmlConfigAndDeepLength(htmlConfig, htmlConfig.getHeaderConfigs());
		}
	}

	@Override
	public String createHtml() throws Exception
	{
		String header = createHeader();
		String boby = createBody();
		StringBuilder result = new StringBuilder();
		result.append("<table width=\"100%\" border=\"1\" cellspacing=\"0\"  cellpadding=\"2\" class=\"datatable htmldatatable ").append(htmlConfig.getTableClass()).append("\" style=\"border-collapse: collapse;\"> ");
		result.append(header).append(boby).append("</table>");
		return result.toString();
	}

	@Override
	public String createHeader() throws Exception
	{
		Assert.isNotNull(htmlConfig, "转换HTML配置信息为空！");
		// 兼容不规则的转换
		if (htmlConfig.isIrregular())
		{
			return ExcelHtmlUtil.NULL_THEAD;
		}
		Assert.isNotNullOrEmpty(htmlConfig.getHeaderConfigs(), "转HTML表头配置信息为空！");
		String result = ExcelHtmlUtil.createHeader(htmlConfig.getHeaderConfigs(), htmlConfig.isWithTitle());
		if (CoreUtil.isEmpty(result))
		{
			return ExcelHtmlUtil.NULL_THEAD;
		}
		if (sameNameCache != null && !sameNameCache.isEmpty())
		{
			// 处理同名问题
			for (Entry<String, String> entry : sameNameCache.entrySet())
			{
				// 替换title和显示名
				result = StringUtil.replace(result, entry.getKey(), entry.getValue());
			}
		}
		return result;
	}

	@Override
	public String createBody() throws Exception
	{
		Assert.isNotNull(htmlConfig, "转换HTML配置信息为空！");
		if (allData == null || allData.isEmpty())
		{
			return ExcelHtmlUtil.NULL_TBODY;
		}
		// 2017 08 23 兼容不规则表
		if (htmlConfig.isIrregular())
		{
			return getDataTableBodyHtml(CombineDataTableUtil.createIrregularTable(htmlConfig.getName(), allData));
		}
		else
		{
			Assert.isNotNullOrEmpty(htmlConfig.getHeaderConfigs(), "转HTML表头配置信息为空！");
			// 获取所有叶子节点
			List<HeaderConfig> allLeafConfigs = htmlConfig.getLeafHeaderConfigs();
			if (allLeafConfigs == null || allLeafConfigs.isEmpty())
			{
				return ExcelHtmlUtil.NULL_TBODY;
			}
			if (htmlConfig.getCombineColumnConfigs() == null || htmlConfig.getCombineColumnConfigs().isEmpty())
			{
				return createRegularBody(allLeafConfigs);
			}
			else
			{
				return createIRegularBody(allLeafConfigs);
			}
		}
	}

	/**
	 * 不规则表
	 * @param allLeftConfigs
	 * @return
	 */
	private String createRegularBody(List<HeaderConfig> allLeafConfigs)
	{
		StringBuilder body = new StringBuilder();

		body.append("<tbody>");

		StringBuilder rowStyle = new StringBuilder();
		StringBuilder tdStyle = null;

		int rowIndex = -1;
		for (Map<String, Object> row : allData)
		{
		    rowIndex ++;
		    if (rowIndex < startIndex)
            {
                continue;
            } else if (rowIndex >= endIndex)
            {
                break;
            }
		    rowStyle.setLength(0);
			// cb 2017 03 01 如果设置了行样式，则添加行样式
			if (!CoreUtil.isEmpty(row.get(TRSTYLE)))
			{
			    rowStyle.append(" style=\"" + CoreUtil.parseStr(row.get(TRSTYLE)) + "\" ");
			}
			if (!CoreUtil.isEmpty(row.get(TRCLASS)))
            {
                rowStyle.append(" class=\"" + CoreUtil.parseStr(row.get(TRCLASS)) + "\" ");
            }
			// cb 2017 03 01新增事件支持
			body.append("<tr").append(rowStyle).append("");
			if (!CoreUtil.isEmpty(row.get(TRCLICK)))
			{
				body.append(" onclick=\"").append(CoreUtil.parseStr(row.get(TRCLICK))).append("\" ");
			}
			body.append(">");
			for (HeaderConfig headerConfig : allLeafConfigs)
			{
			    // 20180109
			    if (!headerConfig.isDisplay())
                {
                    continue;
                }
				tdStyle = new StringBuilder();
				if (HeaderConfig.LEFT.equals(headerConfig.getAlign()))
				{
					if (!headerConfig.isDisplay())
					{
						tdStyle.append(" style=\"display:none;text-align:left; ");
					}
					else
					{
						tdStyle.append(" style=\"text-align:left; ");
					}
				}
				else if (HeaderConfig.RIGHT.equals(headerConfig.getAlign()))
				{
					if (!headerConfig.isDisplay())
					{
						tdStyle.append(" style=\"display:none;text-align:right; ");
					}
					else
					{
						tdStyle.append(" style=\"text-align:right; ");
					}
				}
				else
				{
					if (!headerConfig.isDisplay())
					{
						tdStyle.append(" style=\"display:none;text-align:center; ");
					}
					else
					{
						tdStyle.append(" style=\"text-align:center; ");
					}
				}
				// 看有没有手动设置样式
				if (!CoreUtil.isEmpty(row.get(headerConfig.getValue() + TDSTYLE)))
				{
					tdStyle.append(CoreUtil.parseStr(row.get(headerConfig.getValue() + TDSTYLE)));
				}
				tdStyle.append("\" ");
				// calss error 的支持
				if (!CoreUtil.isEmpty(row.get(headerConfig.getValue() + TDCLASS)))
				{
					tdStyle.append(" class = \"").append(CoreUtil.parseStr(row.get(headerConfig.getValue() + TDCLASS))).append("\" ");
				}
				if (!CoreUtil.isEmpty(row.get(headerConfig.getValue() + TDRED)))
				{
					tdStyle.append(" rederror = \"").append(CoreUtil.parseStr(row.get(headerConfig.getValue() + TDRED))).append("\" ");
				}
				if (!CoreUtil.isEmpty(row.get(headerConfig.getValue() + TDBLUE)))
				{
					tdStyle.append(" blueerror = \"").append(CoreUtil.parseStr(row.get(headerConfig.getValue() + TDBLUE))).append("\" ");
				}
				if (row.get(headerConfig.getValue() + TDDATAMAP) != null)
                {
				    Map<?, ?> map =  (Map<?, ?>) row.get(headerConfig.getValue() + TDDATAMAP);
				    if (!CoreUtil.isEmpty(map))
                    {
                        for (Entry<?, ?> entry : map.entrySet())
                        {
                            tdStyle.append(entry.getKey()).append("  = \"").append(CoreUtil.parseStr(entry.getValue())).append("\" ");
                        }
                    }
                }
				if (htmlConfig.isWithTitle())
				{
					body.append("<td title=\"").append(ExcelHtmlUtil.parseStr(row.get(headerConfig.getValue()))).append("\" ");
				}
				else
				{
					body.append("<td ");
				}
				body.append(" align=\"").append(CoreUtil.isEmpty(headerConfig.getAlign()) ? HeaderConfig.CENTER : headerConfig.getAlign()).append("\" ");
				body.append(tdStyle).append(" >");
				// 2017 03 13 新增事件支持
				if (CoreUtil.isEmpty(row.get(headerConfig.getValue() + TDCLICK)))
				{
					body.append(ExcelHtmlUtil.parseStr(row.get(headerConfig.getValue())));
				}
				else
				{
					body.append("<a class=\"tdlink\" href=\"javascript:void(0)\"").append(" onclick=\"").append(CoreUtil.parseStr(row.get(headerConfig.getValue() + TDCLICK))).append("\" >").append(ExcelHtmlUtil.parseStr(row.get(headerConfig.getValue()))).append("</a>");
				}
				body.append("</td>");
			}
			body.append("</tr>");
		}
		body.append("</tbody>");
		return body.toString();
	}

	/**
	 * 规则表
	 * @param allLeftConfigs
	 * @return
	 * @throws Exception
	 */
	private String createIRegularBody(List<HeaderConfig> allLeafConfigs) throws Exception
	{
		DataTable iregular = CombineDataTableUtil.combineDataTable(allLeafConfigs, allData, htmlConfig.getCombineColumnConfigs());
		if (iregular == null)
		{
			return "";
		}
		// 获取表体
		return getDataTableBodyHtml(iregular);
	}

	private String getDataTableBodyHtml(DataTable iregular)
	{
		StringBuilder result = new StringBuilder();
		result.append("<tbody>");
		// 分类，将所有数据，按行号分类
		Map<String, List<DataRow>> rowMap = classDataTable(iregular);
		if (rowMap != null && !rowMap.isEmpty())
		{
			createBodyRow(rowMap, result);
		}
		result.append("</tbody>");
		return result.toString();
	}

	/**
	 * 构建行Html
	 * @param rowMap
	 * @param result
	 */
	private void createBodyRow(Map<String, List<DataRow>> rowMap, StringBuilder result)
	{
		// 查找最大行
		int maxRow = 0;
		for (Entry<String, List<DataRow>> entry : rowMap.entrySet())
		{
			if (CoreUtil.parseInt(entry.getKey()) > maxRow)
			{
				maxRow = CoreUtil.parseInt(entry.getKey());
			}
		}
		List<DataRow> rows = null;

		for (int i = 0; i < maxRow + 1; i++)
		{
			rows = rowMap.get(i + "");
			if (rows == null || rows.isEmpty())
			{
			    // 处理全部是空的情况, 出现了空行的情况, 特殊
                result.append("<tr></tr>");
				continue;
			}
			// 按照列号排序
			Collections.sort(rows, new Comparator<DataRow>()
			{
				@Override
				public int compare(DataRow row1, DataRow row2)
				{
					return CoreUtil.parseInt(row1.getItemMap().get(ExcelHtmlUtil.COLINDEX)) - CoreUtil.parseInt(row2.getItemMap().get(ExcelHtmlUtil.COLINDEX));
				}
			});

			String trClass = CoreUtil.parseStr(rows.get(0).getItemMap().get(ExcelHtmlUtil.TRCLASS));
			result.append("<tr");
			if (!CoreUtil.isEmpty(trClass))
            {
			    result.append(" class=\"").append(trClass).append("\" ");
            }
			result.append(">");
			for (DataRow dataRow : rows)
			{
				result.append("<td rowspan=\"").append(CoreUtil.parseInt(dataRow.getItemMap().get(ExcelHtmlUtil.MERGEROWCOUNT))).append("\" colspan=\"")
				        .append(CoreUtil.parseInt(dataRow.getItemMap().get(ExcelHtmlUtil.MERGECOLCOUNT))).append("\" ")
				        .append(!htmlConfig.isWithTitle() ? "" : " title=\"" + ExcelHtmlUtil.parseStr(dataRow.getItemMap().get(ExcelHtmlUtil.DATA)) + "\" ")
				        .append(ExcelHtmlUtil.parseStr(dataRow.getItemMap().get(ExcelHtmlUtil.STYLE))).append(CoreUtil.parseStr(dataRow.getItemMap().get(ExcelHtmlUtil.CLASS)))
				        .append(CoreUtil.parseStr(dataRow.getItemMap().get(ExcelHtmlUtil.REDERROR)))
				        .append(CoreUtil.parseStr(dataRow.getItemMap().get(ExcelHtmlUtil.BLUEERROR))).append(" align=\"")
						.append(CoreUtil.isEmpty(dataRow.getItemMap().get(ExcelHtmlUtil.ALIGN)) ? HeaderConfig.CENTER : CoreUtil.parseStr(dataRow.getItemMap().get(ExcelHtmlUtil.ALIGN))).append("\" >");
				
				if (dataRow.getItemMap().get(ExcelHtmlUtil.TDDATAMAP) != null)
                {
                    Map<?, ?> map =  (Map<?, ?>) dataRow.getItemMap().get(ExcelHtmlUtil.TDDATAMAP);
                    if (!CoreUtil.isEmpty(map))
                    {
                        for (Entry<?, ?> entry : map.entrySet())
                        {
                            result.append(entry.getKey()).append("  = \"").append(CoreUtil.parseStr(entry.getValue())).append("\" ");
                        }
                    }
                }
				// 2017 03 13 新增事件的支持
				if (CoreUtil.isEmpty(dataRow.getItemMap().get(ExcelHtmlUtil.CLICK)))
				{
					result.append(ExcelHtmlUtil.parseStr(dataRow.getItemMap().get(ExcelHtmlUtil.DATA)));
				}
				else
				{
					result.append("<a class=\"tdlink\" href=\"javascript:void(0)\"").append(" onclick=\"")
					.append(CoreUtil.parseStr(dataRow.getItemMap().get(ExcelHtmlUtil.CLICK))).append("\" >")
					.append(ExcelHtmlUtil.parseStr(dataRow.getItemMap().get(ExcelHtmlUtil.DATA))).append("</a>");
				}
				result.append("</td>");
			}
			result.append("</tr>");
		}

	}

	/**
	 * 分类DataTable
	 * @param
	 * @param dataTable
	 * @returnallLeafConfigs
	 */
	private static Map<String, List<DataRow>> classDataTable(DataTable dataTable)
	{
		if (dataTable == null || dataTable.getRows().isEmpty())
		{
			return null;
		}
		Map<String, List<DataRow>> result = new HashMap<String, List<DataRow>>();
		String rowIndex = "";
		for (DataRow row : dataTable.getRows())
		{
			rowIndex = CoreUtil.parseStr(row.getItemMap().get(ExcelHtmlUtil.ROWINDEX));
			if (result.containsKey(rowIndex))
			{
				result.get(rowIndex).add(row);
			}
			else
			{
				List<DataRow> rows = new ArrayList<DataRow>();
				rows.add(row);
				result.put(rowIndex, rows);
			}
		}
		return result;
	}

	@SuppressWarnings("unchecked")
	private void convertObject(List<?> allObjectData)
	{
		allData = new ArrayList<Map<String, Object>>();
		if (allObjectData == null || allObjectData.isEmpty())
		{
			return;
		}
		Map<String, Object> map = null;
		PropertyDescriptor pd = null;
		Method method = null;
		for (Object object : allObjectData)
		{
			if (object instanceof Map<?, ?>)
			{
				map = (Map<String, Object>) object;
				allData.add(map);
			}
			else
			{
				// 获取 object的所有属性
				map = new HashMap<String, Object>();
				for (Field field : object.getClass().getDeclaredFields())
				{
					try
					{
						pd = new PropertyDescriptor(field.getName(), object.getClass());
					}
					catch (Exception e)
					{
						// 对于不能获取的字段，如 serialVersionUID 则不处理
						continue;
					}
					method = pd.getReadMethod();
					if (method != null)
					{
						try
						{
							map.put(field.getName(), method.invoke(object));
						}
						catch (IllegalArgumentException e)
						{
						}
						catch (IllegalAccessException e)
						{
						}
						catch (InvocationTargetException e)
						{
						}
					}
				}
				allData.add(map);
			}
		}
	}

	/**
	 * 建造
	 * @author cuibo
	 */
	public static class Builder
	{
		private String configId;
		private HtmlConfig htmlConfig;
		private List<?> allObjectData;
		private Map<String, String> sameNameCache;
	    private int startIndex = -1;
	    private int endIndex = Integer.MAX_VALUE;

		public Builder setConfigId(String configId)
		{
			this.configId = configId;
			return this;
		}

		public Builder setHtmlConfig(HtmlConfig htmlConfig)
		{
			this.htmlConfig = htmlConfig;
			return this;
		}

		public Builder setAllObjectData(List<?> allObjectData)
		{
			this.allObjectData = allObjectData;
			return this;
		}

		public Builder setSameNameCache(Map<String, String> sameNameCache)
		{
			this.sameNameCache = sameNameCache;
			return this;
		}

		public Builder setStartIndex(int startIndex)
        {
            this.startIndex = startIndex;
            return this;
        }

        public Builder setEndIndex(int endIndex)
        {
            this.endIndex = endIndex;
            return this;
        }

        public ExportData2Html build()
		{
			return new ExportData2Html(this);
		}
	}

}
